#include "drvSD.h"
#include "stm32f7xx_hal.h"

#define MSD_OK ((uint8_t)0x00)
#define MSD_ERROR ((uint8_t)0x01)

SD_HandleTypeDef uSdHandle;
DMA_HandleTypeDef dma_rx_handle;
DMA_HandleTypeDef dma_tx_handle;
SDSemCtrlFun drvSdSemCtrl = NULL;
SD_DECT_E SdDectState = SD_DECT_UNEXIST;

uint8_t drvSDDectInit(void)
{
    GPIO_InitTypeDef  gpio_init_structure;
    __HAL_RCC_GPIOC_CLK_ENABLE();
    gpio_init_structure.Pin = GPIO_PIN_13;
    gpio_init_structure.Mode = GPIO_MODE_INPUT;
    gpio_init_structure.Pull = GPIO_PULLUP;
    gpio_init_structure.Speed = GPIO_SPEED_HIGH;
    HAL_GPIO_Init(GPIOC, &gpio_init_structure);
    return MSD_OK;
}

uint8_t drvSDInit(SDSemCtrlFun ctrl)
{
    GPIO_InitTypeDef gpio_init_structure;

    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_GPIOD_CLK_ENABLE();

    /* Common GPIO configuration */
    gpio_init_structure.Mode = GPIO_MODE_AF_PP;
    gpio_init_structure.Pull = GPIO_PULLUP;
    gpio_init_structure.Speed = GPIO_SPEED_HIGH;
    gpio_init_structure.Alternate = GPIO_AF12_SDMMC1;
    gpio_init_structure.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12;
    HAL_GPIO_Init(GPIOC, &gpio_init_structure);
    gpio_init_structure.Pin = GPIO_PIN_2;
    HAL_GPIO_Init(GPIOD, &gpio_init_structure);

    __HAL_RCC_SDMMC1_CLK_ENABLE();
    __HAL_RCC_DMA2_CLK_ENABLE();
    uint8_t sd_state = MSD_OK;
    uSdHandle.Instance = SDMMC1;
    uSdHandle.Init.ClockEdge = SDMMC_CLOCK_EDGE_RISING;
    uSdHandle.Init.ClockBypass = SDMMC_CLOCK_BYPASS_DISABLE;
    uSdHandle.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE;
    uSdHandle.Init.BusWide = SDMMC_BUS_WIDE_1B;
    uSdHandle.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE;
    uSdHandle.Init.ClockDiv = SDMMC_TRANSFER_CLK_DIV;

    if(HAL_SD_Init(&uSdHandle) != HAL_OK) {
        sd_state = MSD_ERROR;
    }

    /* Configure DMA Rx parameters */
    dma_rx_handle.Init.Channel = DMA_CHANNEL_4;
    dma_rx_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;
    dma_rx_handle.Init.PeriphInc = DMA_PINC_DISABLE;
    dma_rx_handle.Init.MemInc = DMA_MINC_ENABLE;
    dma_rx_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    dma_rx_handle.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    dma_rx_handle.Init.Mode = DMA_PFCTRL;
    dma_rx_handle.Init.Priority = DMA_PRIORITY_VERY_HIGH;
    dma_rx_handle.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
    dma_rx_handle.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
    dma_rx_handle.Init.MemBurst = DMA_MBURST_INC4;
    dma_rx_handle.Init.PeriphBurst = DMA_PBURST_INC4;
    dma_rx_handle.Instance = DMA2_Stream3;
    __HAL_LINKDMA(&uSdHandle, hdmarx, dma_rx_handle);
    HAL_DMA_DeInit(&dma_rx_handle);
    HAL_DMA_Init(&dma_rx_handle);

    /* Configure DMA Tx parameters */
    dma_tx_handle.Init.Channel = DMA_CHANNEL_4;
    dma_tx_handle.Init.Direction = DMA_MEMORY_TO_PERIPH;
    dma_tx_handle.Init.PeriphInc = DMA_PINC_DISABLE;
    dma_tx_handle.Init.MemInc = DMA_MINC_ENABLE;
    dma_tx_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    dma_tx_handle.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    dma_tx_handle.Init.Mode = DMA_PFCTRL;
    dma_tx_handle.Init.Priority = DMA_PRIORITY_VERY_HIGH;
    dma_tx_handle.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
    dma_tx_handle.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
    dma_tx_handle.Init.MemBurst = DMA_MBURST_INC4;
    dma_tx_handle.Init.PeriphBurst = DMA_PBURST_INC4;
    dma_tx_handle.Instance = DMA2_Stream6;
    __HAL_LINKDMA(&uSdHandle, hdmatx, dma_tx_handle);
    HAL_DMA_DeInit(&dma_tx_handle);
    HAL_DMA_Init(&dma_tx_handle); 

    HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 0x0F, 0);
    HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn);
    HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 0x0F, 0);
    HAL_NVIC_EnableIRQ(DMA2_Stream6_IRQn);

    HAL_NVIC_SetPriority(SDMMC1_IRQn, 0x0E, 0);
    HAL_NVIC_EnableIRQ(SDMMC1_IRQn);

    if(sd_state == MSD_OK)
    {
        if(HAL_SD_ConfigWideBusOperation(&uSdHandle, SDMMC_BUS_WIDE_4B) != HAL_OK) {
            sd_state = MSD_ERROR;
        } else {
            sd_state = MSD_OK;
        }
    }
    drvSdSemCtrl = ctrl;
    return  sd_state;
}

void drvSDDeInit(void)
{
    HAL_SD_DeInit(&uSdHandle);
    HAL_NVIC_DisableIRQ(DMA2_Stream3_IRQn);
    HAL_NVIC_DisableIRQ(DMA2_Stream6_IRQn);
    HAL_DMA_DeInit(&dma_rx_handle);
    HAL_DMA_DeInit(&dma_tx_handle);
    HAL_NVIC_DisableIRQ(SDMMC1_IRQn);
    __HAL_RCC_SDMMC1_CLK_DISABLE();
    __HAL_RCC_DMA2_CLK_DISABLE();
}

SD_DECT_E drvSDDetected(void)
{
    switch (SdDectState) {
        case SD_DECT_UNEXIST:
            if(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13) == GPIO_PIN_SET) {
                SdDectState = SD_DECT_PLUG;
            }
            break;
        case SD_DECT_PLUG:
            SdDectState = SD_DECT_EXIST;
            break;
        case SD_DECT_EXIST:
            if(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13) == GPIO_PIN_RESET) {
                SdDectState = SD_DECT_UNPLUG;
            }
            break;
        case SD_DECT_UNPLUG:
            SdDectState = SD_DECT_UNEXIST;
            break;
        default:
            break;
    }
    return SdDectState;
}

uint8_t drvSDReadBlocks(uint32_t *pData, uint32_t ReadAddr, uint32_t NumOfBlocks)
{
    HAL_StatusTypeDef ret = HAL_ERROR;
    drvSdSemCtrl(SD_CTRL_READ, true);
    ret = HAL_SD_ReadBlocks_DMA(&uSdHandle, (uint8_t *)pData, ReadAddr, NumOfBlocks);
    return (ret!= HAL_OK) ? MSD_ERROR : MSD_OK;
}

uint8_t drvSDWriteBlocks(uint32_t *pData, uint32_t WriteAddr, uint32_t NumOfBlocks)
{
    HAL_StatusTypeDef ret = HAL_ERROR;
    drvSdSemCtrl(SD_CTRL_WRITE, true);
    ret = HAL_SD_WriteBlocks_DMA(&uSdHandle, (uint8_t *)pData, WriteAddr, NumOfBlocks);
    return (ret!= HAL_OK) ? MSD_ERROR : MSD_OK;
}

uint8_t drvSDErase(uint32_t StartAddr, uint32_t EndAddr)
{
    return (HAL_SD_Erase(&uSdHandle, StartAddr, EndAddr) != HAL_OK) ? MSD_ERROR : MSD_OK;
}

uint8_t drvSDGetCardState(void)
{
    return((HAL_SD_GetCardState(&uSdHandle) == HAL_SD_CARD_TRANSFER ) ? 1 : 0);
}

void drvSDGetCardInfo(HAL_SD_CardInfoTypeDef *CardInfo)
{
    HAL_SD_GetCardInfo(&uSdHandle, CardInfo);
}

void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)
{
    drvSdSemCtrl(SD_CTRL_WRITE, false);
}

void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)
{
    drvSdSemCtrl(SD_CTRL_READ, false);
}

void SDMMC1_IRQHandler(void)
{
    HAL_SD_IRQHandler(&uSdHandle);
}

void DMA2_Stream3_IRQHandler(void)
{
    HAL_DMA_IRQHandler(&dma_rx_handle);
}

void DMA2_Stream6_IRQHandler(void)
{
    HAL_DMA_IRQHandler(&dma_tx_handle);
}
